home *** CD-ROM | disk | FTP | other *** search
/ Fritz: All Fritz / All Fritz.zip / All Fritz / FILES / SPEEEECH / MIMIC11.LZH / MIMIC.C < prev    next >
Text File  |  1990-03-13  |  16KB  |  254 lines

  1. /* MIMIC.C  Version 1.0                                                      */
  2. /* (c) 1990 Eletech Electronics, Inc. All rights UNreserved.                 */
  3. /*                                                                           */
  4. /* Creation : 3-13-90 by Wei Lu.                                             */
  5. /* Function : Works with MINT.ASM to play Eletech Digicorder recorded voice  */
  6. /*            file thru PC's internal speaker at specified sampling rate.    */
  7. /* Usage    : MIMIC sampling_rate filename                                   */
  8. /* Compiler : Microsoft C 5.0 and up.                                        */
  9. /*            Compiling Options: None.                                       */
  10. /*                                                                           */
  11. /* Operation: First MIMIC.C prepares for playback by:                        */
  12. /*                                                                           */
  13. /*            1. allocating 64K bytes of play buffer memory from DOS,        */
  14. /*            2. loading voice data into play buffer bank 1,                 */
  15. /*            3. re-directing (intercepting) timer 0 interrupt vector,       */
  16. /*            4. re-programming timer 0 to desired sampling rate.            */
  17. /*                                                                           */
  18. /*            At this point MINT.ASM should start sending voice data to the  */
  19. /*            speaker bit by bit upon each timer 0 interrupt. Meanwhile      */
  20. /*            MIMIC.C will try to load more data into play buffer bank 2     */
  21. /*            and, if any key is pressed, stop playing. After bank 1 is      */
  22. /*            played up, MIMIC.C switches playing to bank 2 and tries        */
  23. /*            loading more data into bank 1. This goes on until all data are */
  24. /*            played and then MIMIC.C stops playing.                         */
  25. /*                                                                           */
  26. /*            When the playback is stopped (either by a key press or by      */
  27. /*            running out of voice data) MIMIC restores timer 0 and its      */
  28. /*            interrupt vector to normal operation. At this point, MINT.ASM  */
  29. /*            is unhooked from timer 0 interrupt and its operation stopped.  */
  30. /*                                                                           */
  31. /*            MIMIC.C communicates with MINT.ASM thru the following shared   */
  32. /*            variables:                                                     */
  33. /*                                                                           */
  34. /*            play_addr   - the far pointer points to the start address of   */
  35. /*                          the 64KB play buffer MIMIC.C allocates from DOS. */
  36. /*                          Set by MIMIC.C and read by MINT.ASM to find out  */
  37. /*                          where the voice data are. Note that its offset   */
  38. /*                          address is always 0 since DOS allocates memory   */
  39. /*                          in paragraphs (16 bytes.)                        */
  40. /*                                                                           */
  41. /*            play_ptr    - the pointer points to the currently-been-played  */
  42. /*                          byte. Starts from 0 and may go as high as 64K-1. */
  43. /*                          Initially set by MIMIC.C and subsequently        */
  44. /*                          updated by MINT.ASM.                             */
  45. /*                                                                           */
  46. /*            play_length - number of bytes left to play. Initially set by   */
  47. /*                          MIMIC.C and subsequently updated by MINT.ASM.    */
  48. /*                                                                           */
  49. /*            tack_count  - number of "tacks" to wait before MINT.ASM does a */
  50. /*                          real clock interrupt. Calculated and set by      */
  51. /*                          MIMIC.C and read by MINT.ASM.                    */
  52. /*                                                                           */
  53. /*            Here a "tack" is eight "ticks" and a "tick" is a timer 0       */
  54. /*            interrupt. When a system is powered up or reset, DOS sets up   */
  55. /*            timer 0 for clock interrupt at about 18.2 times per second (so */
  56. /*            the tick rate is 18.2 Hz at this moment.) This is achieved by  */
  57. /*            dividing 1.19 MHz (input clock rate) by 65536. MIMIC.C         */
  58. /*            re-programs timer 0 so that the tick rate equals the sampling  */
  59. /*            rate. This can be achieved by dividing 1.19 MHz by the integer */
  60. /*            part of 1190000/sampling_rate. This integer is kept in the     */
  61. /*            variable "timer_div" and used to re-program timer 0. Although  */
  62. /*            the tick rate has been changed, MINT.ASM still has to do 18.2  */
  63. /*            "real" clock interrupts every second to keep the clock         */
  64. /*            running. A little calculation (65536*sampling_rate/1190000)    */
  65. /*            yields the number of ticks to wait between "real" clock        */
  66. /*            interrupts. Since MINT.ASM only checks this value at the end   */
  67. /*            of every byte (or 8 ticks), this value is further divided by 8 */
  68. /*            to become number of "tacks" (variable tack_count.)                                   */
  69. /*                                                                           */
  70. /*            Note that for most sampling rates the above calculations do    */
  71. /*            not yield integers without remainders. If this is the case     */
  72. /*            then the clock that DOS keeps will go slight faster (since we  */
  73. /*            always round down the quotient) during the execution of the    */
  74. /*            program. This deviation equals 1 - timer_div*tack_count/8192   */
  75. /*            and is usually less than 1%. This effect will accumulate with  */
  76. /*            each run of the program until the system is reset (cold or     */
  77. /*            warm), at that point DOS will reset the time by reading from   */
  78. /*            either the hardware clock or the user, both not affected by    */
  79. /*            this program, hopefully.                                       */
  80. /*                                                                           */
  81. /*                                                                           */
  82. /*            There is actually a chance that the DOS clock goes slightly    */
  83. /*            slower during the execution of the program. To explain this,   */
  84. /*            we will have to first explain how MINT.ASM works. Upon each    */
  85. /*            tick interrupt, MINT.ASM is activated and does one of the      */
  86. /*            following:                                                     */
  87. /*                                                                           */
  88. /*            a. If there is no more data to play, do a "real" clock         */
  89. /*               interrupt if it's time to, then terminate.                  */
  90. /*                                                                           */
  91. /*            Otherwise,                                                     */
  92. /*                                                                           */
  93. /*            b. If it does not need a new byte of data, output a bit and    */
  94. /*               terminate. (This takes the least amount of time.)           */
  95. /*                                                                           */
  96. /*            c. If it needs a new byte of data but it's not time yet to do  */
  97. /*               a "real" clock interrupt, get a byte and output a bit and   */
  98. /*               terminate. (This takes a little bit more time than a.)      */
  99. /*                                                                           */
  100. /*            d. If it needs a new byte of data and it's also time to do a   */
  101. /*               "real" clock interrupt, get a byte, output a bit, do a      */
  102. /*               "real" clock interrupt and terminate. (This takes a lot of  */
  103. /*               and the most amount of time.)                               */
  104. /*                                                                           */
  105. /*            So in normal operation the system has to complete the worst    */
  106. /*            case (case d) within a sample period. If the system is slow so */
  107. /*            that it misses at least a tick interrupt while doing case d,   */
  108. /*            an 18.2 Hz abnormality is introduced (which you can barely     */
  109. /*            notice.) If the system is even slower that it misses at least  */
  110. /*            a tick interrupt while doing case c, the playback tone will be */
  111. /*            slightly lower than normal due to slower effective playback    */
  112. /*            rate. If the system is so slow as to miss at least a tick      */
  113. /*            interrupt while doing case b, the tone will be significantly   */
  114. /*            lower due to much slower effective playback rate. Case a is    */
  115. /*            usually no problem except when it's time to do a "real" clock  */
  116. /*            interrupt, at that time tick interrupts can be missed and the  */
  117. /*            18.2 Hz abnormality occurs.                                    */
  118. /*                                                                           */
  119. /*            All these abnormalities work towards slowing down the DOS      */
  120. /*            clock. Being slow to a certain extent (having only the 18.2 Hz */
  121. /*            abnormality) is sometimes a blessing since it works to offset  */
  122. /*            the clock speedup effect as mentioned above.                   */
  123. /*                                                                           */
  124. /*            Also note that the playback is non-destructive, meaning that   */
  125. /*            to play the same data again all you have to do is reset the    */
  126. /*            play_ptr and play_length. Part or all of the recording can be  */
  127. /*            instantly replayed in this manner.                             */
  128. /*                                                                           */
  129. /*            A final warning is that this program not only re-directs clock */
  130. /*            interrupt, but also borrowed software interrupt #255. Certain  */
  131. /*            TSR programs hooking to these two interrupts may not work      */
  132. /*            correctly after running this program. You can, of course,      */
  133. /*            change the new clock interrupt to something other than #255    */
  134. /*            should conflict results.                                       */
  135.  
  136. #include <stdio.h>
  137. #include <dos.h>
  138. #include <fcntl.h>
  139.  
  140. #define CLOCK_IRQ 8                         /* old clock IRQ = vector #8     */
  141. #define NEW_CLOCK_IRQ 255                   /* new clock IRQ = vector #255   */
  142. #define TIMER_COUNT 64                      /* 8253 "load timer 0" I/O port  */
  143. #define TIMER_MODE 67                       /* 8253 "write mode" I/O port    */
  144.  
  145. main(argc, argv)
  146. int argc;
  147. char **argv;
  148. {
  149.  
  150. extern void interrupt far int_handler();    /* new clock interrupt handler   */
  151. extern char far *play_addr;                 /* play buffer start address     */
  152. extern unsigned play_ptr;                   /* play byte pointer             */
  153. extern unsigned play_length;                /* play length (in byte)         */
  154. extern unsigned char tack_count;            /* how often we do old clock int */
  155.  
  156. void (interrupt far *old_vector1)();        /* where we put old timer vector */
  157. void (interrupt far *old_vector2)();        /* where we put old vector #255  */
  158. unsigned buffer_seg;                        /* play buffer segment address   */
  159. unsigned rate;                              /* rate = sampling_rate          */
  160. unsigned fhandle, bytes_read, timer_div, current_bank;
  161. char filename[80];
  162. char far *p_addr1;                          /* points to start of bank 1     */
  163. char far *p_addr2;                          /* points to start of bank 2     */
  164.  
  165. if (_dos_allocmem(4096, &buffer_seg))       /* allocate 64KB of play buffer  */
  166.    {
  167.    printf("MIMIC ERROR: can not allocate 64K bytes of buffer memory\n");
  168.    exit(0);
  169.    }
  170.  
  171. FP_SEG(play_addr) = FP_SEG(p_addr1) = FP_SEG(p_addr2) = buffer_seg;
  172. FP_OFF(play_addr) = FP_OFF(p_addr1) = 0;    /* bank 1 from 0 to 32K          */
  173. FP_OFF(p_addr2) = 32768;                    /* bank 2 from 32K to 64K        */
  174.  
  175. if (argc != 3)
  176.    {
  177.    printf("MIMIC USAGE: MIMIC sampling_rate filename\n");
  178.    exit(0);
  179.    }
  180. if ((sscanf(argv[1], "%d", &rate) == 0) || (rate < 12) || (rate > 64))
  181.    {
  182.    printf("MIMIC USAGE: sampling_rate must be an integer between 12 and 64\n");
  183.    exit(0);
  184.    }
  185. if (_dos_open(argv[2], O_RDONLY, &fhandle))
  186.    {
  187.    printf("MIMIC ERROR: can not open file '%s'\n", argv[2]);
  188.    exit(0);
  189.    }
  190.  
  191. _dos_read(fhandle, (void far *)p_addr1, 32768, &bytes_read);
  192. current_bank = 1;
  193. play_ptr = 0;                               /* start from the first byte     */
  194. play_length = bytes_read;
  195. timer_div = 1190/rate;                      /* sampling_rate is in KHz       */
  196. tack_count = 8192*rate/1190;                /* see below                     */
  197.  
  198. /* How often should we do an real clock interrupt? Once every 65536 ticks,   */
  199. /* or 8192 tacks, when the counting frequency is 1190 KHz. Now the counting  */
  200. /* frequency is "rate" in KHz, we should do it every 8192*rate/1190 tacks.   */
  201.  
  202. old_vector1 = _dos_getvect(CLOCK_IRQ);      /* get old clock int vector      */
  203. old_vector2 = _dos_getvect(NEW_CLOCK_IRQ);  /* get old vector #255           */
  204. _dos_setvect(NEW_CLOCK_IRQ, old_vector1);   /* relocate old clock int handler*/
  205. _dos_setvect(CLOCK_IRQ, int_handler);       /* intercept clock interrupts    */
  206.  
  207. outp(TIMER_MODE, 0x36);                     /* prepare to load new count     */
  208. outp(TIMER_COUNT, (char)timer_div);         /* load low byte of new count    */
  209. outp(TIMER_COUNT, 0x0);                     /* high byte should be 0         */
  210.  
  211. /* MINT.ASM now starts sending data to speaker on each timer 0 interrupt     */
  212.  
  213. while (bytes_read != 0)
  214.    {
  215.    if (bytes_read == 32768)                 /* there might be more data      */
  216.       {
  217.       if (current_bank == 1)
  218.          _dos_read(fhandle, (void far *)p_addr2, 32768, &bytes_read);
  219.       else
  220.          _dos_read(fhandle, (void far *)p_addr1, 32768, &bytes_read);
  221.       }
  222.    else
  223.       bytes_read = 0;                       /* no more data to play          */
  224.  
  225.    while (play_length != 0)
  226.       {
  227.       if (kbhit())                          /* terminate if any key is hit   */
  228.          break;                             /* get out of this small loop    */
  229.       }
  230.    if (kbhit())
  231.       {
  232.       getch();                              /* remove the unneeded "any key" */
  233.       break;                                /* get out of this big loop      */
  234.       }
  235.    if (current_bank == 1)
  236.       {
  237.       play_ptr = 32768;                     /* starts from bank 2 now        */
  238.       current_bank = 2;
  239.       }
  240.    else
  241.       {
  242.       play_ptr = 0;                         /* restarts from bank 1 now      */
  243.       current_bank = 1;
  244.       }
  245.    play_length = bytes_read;
  246.    }
  247.  
  248. outp(TIMER_MODE, 0x36);                     /* prepare to restore count      */
  249. outp(TIMER_COUNT, 0);                       /* old count = 0 (actually 65536)*/
  250. outp(TIMER_COUNT, 0);
  251. _dos_setvect(CLOCK_IRQ, old_vector1);       /* restore clock int vector      */
  252. _dos_setvect(NEW_CLOCK_IRQ, old_vector2);   /* restore int vector #255       */
  253. }
  254.